<template>
  <div id="globe" ref="canvas"
       @mousedown="onMouseDown"
       @mouseup="onMouseUp"
       @mousemove="onMouseMove"
       @mouseover="onMouseOver"
       @mouseleave="onMouseOut">
  </div>
</template>

<script>
import * as THREE from 'three'

export default {
  data() {
    const items = []
    for (let i = 0; i < 5; i++) {
      items.push({
        name: `第${i + 1}个项的名称`,
        icon: 'icon.png'
      })
    }
    return {
      items,
      itemBalls: [],
      radius: 40,
      mouseover: false,
      mousedown: false,
      lastPos: {
        x: 0,
        y: 0
      },
      textures: {
        circle: null,
        ball: null,
        items: []
      },
      /**
       * @type {THREE.WebGLRenderer}
       */
      renderer: null,
      camera: null,
      scene: null,
      ball: null,
      ringGroup: null,
      currentObj: null,
      materials: {
        origin: null,
        active: new THREE.MeshBasicMaterial({
          color: 0xffff00
        })
      },
      animId: -1,
      resizeTimer: -1
    }
  },
  created() {
    console.debug(`three@${THREE.REVISION}`)
  },
  mounted() {
    this.loadTexture()
    this.render()
    window.addEventListener('resize', this.onWindowResize)
  },
  beforeDestroy() {
    clearTimeout(this.resizeTimer)
    window.removeEventListener('resize', this.onWindowResize)
    if (this.renderer) {
      this.renderer.dispose()
    }
  },
  methods: {
    getContainerSize() {
      const container = this.$refs.canvas
      const containerWidth = container.clientWidth
      const containerHeight = container.clientHeight

      return [containerWidth, containerHeight]
    },
    onWindowResize() {
      if (!this.renderer) {
        return
      }
      clearTimeout(this.resizeTimer)

      this.resizeTimer = setTimeout(() => {
        const [containerWidth, containerHeight] = this.getContainerSize()
        this.renderer.setSize(containerWidth, containerHeight)
        // 重置相机投影的相关参数
        // 窗口宽高比
        this.camera.aspect = window.innerWidth / window.innerHeight
        // 渲染器执行render方法的时候会读取相机对象的投影矩阵属性projectionMatrix
        // 但是不会每渲染一帧，就通过相机的属性计算投影矩阵(节约计算资源)
        // 如果相机的一些属性发生了变化，需要执行updateProjectionMatrix ()方法更新相机的投影矩阵
        this.camera.updateProjectionMatrix()
      }, 100)
    },
    loadTexture() {
      this.textures.circle = new THREE.TextureLoader().load('circle.png')
      this.textures.ball = new THREE.TextureLoader().load('ball.png')
      this.items.forEach((item, idx) => {
        this.textures.items[idx] = new THREE.TextureLoader().load(`/static/icons/${item.icon}`)
      })
    },
    renderItem(data, index) {
      const geometry = new THREE.SphereGeometry(5, 32, 32)
      const material = new THREE.MeshBasicMaterial({
        // map: this.textures.items[index]
      })
      const item = new THREE.Mesh(geometry, material)
      item.name = data.name
      item.userData.type = 'item'

      const angle = 360 * index / this.items.length
      const x = this.radius * (Math.cos(angle * Math.PI / 180))
      const y = this.radius * (Math.sin(angle * Math.PI / 180))
      item.position.set(x, y, 0)
      // item.rotation.x = Math.PI * 0.5
      // item.rotation.z = Math.PI * Math.sin(angle)
      // // FIXME 处理在圆上不同位置的旋转角度
      // group.rotation.x = Math.PI / 2
      // // group.rotation.y = Math.cos(angle * Math.PI / 180)
      return item
    },
    render() {
      const container = this.$refs.canvas
      const [containerWidth, containerHeight] = this.getContainerSize()

      const renderer = new THREE.WebGLRenderer({alpha: true, antialias: true})
      renderer.setSize(containerWidth, containerHeight, false)
      container.appendChild(renderer.domElement)
      this.renderer = renderer

      const camera = new THREE.PerspectiveCamera(45, containerWidth / containerHeight, 1, 500)
      camera.position.set(0, 0, 100)
      camera.lookAt(0, 0, 0)
      this.camera = camera

      const scene = new THREE.Scene()
      this.scene = scene

      const circle = new THREE.Mesh(
          new THREE.CircleGeometry(20, 64),
          new THREE.MeshBasicMaterial({
            transparent: true,
            map: this.textures.circle
          })
      )
      scene.add(circle)
      const ball = new THREE.Mesh(
          new THREE.SphereGeometry(20, 64, 64),
          new THREE.MeshBasicMaterial({
            transparent: true,
            map: this.textures.ball
          })
      )
      this.ball = ball
      scene.add(ball)

      const ringGroup = new THREE.Group()
      this.ringGroup = ringGroup
      scene.add(ringGroup)

      const ring = new THREE.Mesh(
          new THREE.TorusGeometry(this.radius, 0.1, 64, 64),
          new THREE.MeshBasicMaterial({
            color: 0x0000ff,
            opacity: 0.3,
            transparent: true
          })
      )
      ringGroup.add(ring)

      ringGroup.rotateX(Math.PI * 0.58)
      ringGroup.rotateY(Math.PI * 0.06)

      this.items.forEach((item, i) => {
        const a = this.renderItem(item, i)
        ringGroup.add(a)
        this.itemBalls.push(a)
      })

      this.doAnimate()
    },
    doAnimate() {
      if (this.mouseover) {
        return
      }
      cancelAnimationFrame(this.animId)
      this.animId = requestAnimationFrame(this.doAnimate)

      this.ball.rotation.y -= 0.01
      this.ringGroup.rotation.z += 0.001
      // this.itemBalls.forEach(b => {
      //   b.rotation.z -= 0.01
      // })

      this.renderer.render(this.scene, this.camera)
    },
    //获取事件操作对象
    getSelectedObj(plane, raycaster, e) {
      //将html坐标系转化为webgl坐标系，并确定鼠标点击位置
      plane.x = e.clientX / this.renderer.domElement.clientWidth * 2 - 1
      plane.y = -(e.clientY / this.renderer.domElement.clientHeight * 2) + 1
      //以camera为z坐标，确定所点击物体的3D空间位置
      raycaster.setFromCamera(plane, this.camera)
      //确定所点击位置上的物体数量集合
      const intersects = raycaster.intersectObjects(this.scene.children, true)
      //返回连线经过的物体集合
      return intersects[0]
    },
    onMouseOver() {
      this.mouseover = true
    },
    onMouseOut() {
      this.mouseover = false
      this.mousedown = false
      this.doAnimate()
    },
    onMouseMove(e) {
      if (!this.renderer) {
        return
      }
      if (this.mousedown) {
        this.doRotation(e)
        return
      }
      //创建光线投射对象
      const rayCaster = new THREE.Raycaster()
      //创建二维平面
      const plane = new THREE.Vector2()
      // 获取鼠标选中对象集合，e是点击事件对象
      const selected = this.getSelectedObj(plane, rayCaster, e)
      // 清除以前选中的项
      if (this.currentObj) {
        if (this.currentObj === selected) {
          return
        }
        this.currentObj.object.material = this.materials.origin
        this.currentObj = null
      }

      if (!selected) {
        document.body.cursor = 'default'
        return
      }

      const obj = selected.object

      if (!obj.userData || obj.userData.type !== 'item') {
        document.body.cursor = 'default'
        return
      }

      document.body.cursor = 'pointer'
      this.currentObj = selected
      this.materials.origin = obj.material

      obj.material = this.materials.active
    }
  }
}
</script>
<style scoped>
#globe {
  width: 100%;
  height: 100%;
  background: url("./bg.png") no-repeat center center;
  background-size: 100% 100%;
}
</style>
